package twinb;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ecologylab.xml.ElementState;
import ecologylab.xml.xml_inherit;

import twinb.text.Kgram;
import twinb.text.Regex;
import twinb.text.TermVector;

@xml_inherit
public class Conversation extends ElementState implements Comparable<Conversation>
{
    // consts
    public static final int     K              = 3;

    public static final double  wPeople        = 1.0;

    public static final double  wContent       = 1.0;

    public static final double  wHotness       = 1.0;

    // xml data
    @xml_attribute
    public String               count;

    @xml_leaf
    public int                  id;

    @xml_leaf
    public String               category;

    @xml_leaf
    @xml_tag("has_read")
    public String               hasRead;

    @xml_nested
    public Tweet                header;

    @xml_leaf
    @xml_tag("involvers")
    public String               involversString;

    @xml_nowrap
    @xml_collection("tweet")
    public List<Tweet>          tweets		   = new ArrayList<Tweet>();

    // internal data
    private boolean             readMark       = false;
    
    public List<Set<String>>    diction   	   = null;
    
    public Set<Long>			tweetId		   = new HashSet<Long>();

    public List<Person>         involvers      = new ArrayList<Person>();

    public Map<Person, Integer> involversCount = new HashMap<Person, Integer>();

    public Map<Long, Person>    idPersonMap    = null;

    //public List<String>         words          = new ArrayList<String>();

    public List<String>         urls           = new ArrayList<String>();

    //public List<String>         kgrams         = new ArrayList<String>();

    public TermVector           vec            = null;

    public TimeFactor           timeFactor     = new TimeFactor();

    public double               score          = 0.0;

    public double               searchScore         = 0.0;

    public Twinb                host           = null;
    
    public Kgram 				kgramer 	   = new Kgram();
    
    Regex 						re			   = new Regex();

    public boolean getReadMark()
    {
        return readMark;
    }

    public void setReadMark(boolean readMark)
    {
        this.readMark = readMark;
        hasRead = String.valueOf(readMark);
    }

    public Conversation(Twinb host, Tweet header, Map<Long, Person> idPersonMap, int id)
    {
        this.host = host;
        //this.tweets = statuses;
        //this.sortTweets();
        this.idPersonMap = idPersonMap;
        this.setReadMark(false);
        this.header=header;
        // this.setVector();
        this.id=id;
        this.diction=host.diction;
        this.vec = new TermVector(host.diction);
        this.feedTweet(header);
        //setInvolvers();
        //this.reset();
    }
    /*
    public void clear()
    {
    	this.score = 0;
        this.searchScore=0;
        this.vec.clear();
        this.involvers.clear();
        this.involversCount.clear();
        this.words.clear();
        this.urls.clear();
        this.kgrams.clear();
    }
    */
    public void feedTweet(Tweet tweet)
    {
    	this.tweets.add(tweet);
    	this.tweetId.add(tweet.getStatus().id);
    	List<String> newWords=this.kgramer.getKgrams(this.re.getWords(tweet.text));
    	this.diction.get(this.id).addAll(newWords);
    	this.vec.addTerms(newWords);   	
    	this.feedInvolver(tweet);    	
    }
    
    public void feedInvolver(Tweet tweet)
    {
    	long personId = tweet.getStatus().getUser().id;
        Person person = idPersonMap.get(personId);
        if (!involvers.contains(person))
        {
            involvers.add(person);
            involversCount.put(person, 1);
        }
        else
            involversCount.put(person, involversCount.get(person) + 1);
    
        
        involversString = "";
        for (Person p : involversCount.keySet())
        {
            involversString += (involversString.isEmpty()?"":", ") + p.getUser().getName();
        }
    }
/*
    public void reset()
    {
        this.score = 0;
        // this.search=0;
        //this.vec.clear();
        this.involvers.clear();
        this.involversCount.clear();
        this.words.clear();
        this.urls.clear();
        this.kgrams.clear();
        this.setInvolvers();
        this.setVector();
    }
*/
    public void setSearchScore(double searchScore)
    {
        this.searchScore = searchScore;
    }
/*
    public void setKgrams()
    {
        this.setWords();
        
        kgrams = kgramer.getKgrams(this.words);
    }

    public List<String> getKgrams()
    {
        return this.kgrams;
    }
*/
    /*
    public void setWords()
    {
        
        for (int i = 0; i < tweets.size(); ++i)
        {
            List<String> word = re.getWords(tweets.get(i).getStatus().text);
            for (int j = 0; j < word.size(); ++j)
            {
                // System.out.println(word.get(j));
                this.words.add(word.get(j));
            }
        }
    }

    public List<String> getWords()
    {
        return this.words;
    }

    public void setUrls()
    {
        Regex re = new Regex();
        for (int i = 0; i < tweets.size(); ++i)
        {
            List<String> word = re.getUrls(tweets.get(i).getStatus().text);
            for (int j = 0; j < word.size(); ++j)
                this.urls.add(word.get(j));
        }
    }
*/
    public List<String> getUrls()
    {
        return this.urls;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public int getId()
    {
        return this.id;
    }

    public void setInvolvers()
    {
        for (int i = 0; i < tweets.size(); ++i)
        {

            long personId = tweets.get(i).getStatus().getUser().id;
            Person person = idPersonMap.get(personId);
            if (!involvers.contains(person))
            {
                involvers.add(idPersonMap.get(personId));
                involversCount.put(person, 1);
            }
            else
                involversCount.put(person, involversCount.get(person) + 1);
        }
        
        involversString = "";
        for (Person p : involversCount.keySet())
        {
            involversString += (involversString.isEmpty()?"":", ") + p.getUser().getName();
        }
    }

    public List<Person> getInvolvers()
    {
        return this.involvers;
    }

    public Map<Person, Integer> getInvolversTimes()
    {
        return this.involversCount;
    }

    public void sortTweets()
    {
        Collections.sort(this.tweets, new Comparator<Tweet>()
        {
            public int compare(Tweet t1, Tweet t2)
            {
                long ans = (t1.getTime() - t2.getTime());
                if (ans >= 0)
                    return 1;
                else
                    return -1;
            }
        });
        
        count = String.valueOf(tweets.size());
        for (int i = 0; i < tweets.size(); ++i)
        {
            tweets.get(i).setCid(i + 1);
        }
    }

    public double getHotness()
    {
        double duration = 0.0;
        if (this.tweets.size() == 1)
            return 0.0;
        this.sortTweets();
        if (this.tweets.size() >= K)
            duration = tweets.get(K - 1).getTime() - tweets.get(0).getTime();
        else
            duration = (tweets.get(tweets.size() - 1).getTime() - tweets.get(0).getTime())
                    / (1.0 * tweets.size()) * (1.0 * K);

        return (1.0 * tweets.size()) / duration;
    }

    public double getRankScore()
    {
        return this.score;
    }

    public List<Tweet> getTweets()
    {
        return this.tweets;
    }

    public void setRankScore()
    {
        //this.reset();
        double people = 0.0, content = 0.0, hotness = 0.0;

        content = this.host.cim.getVector().cos(this.vec);
        for (Person ppl : this.involversCount.keySet())
            people += host.pim.getInterest(ppl.getId()) * this.involversCount.get(ppl);
        this.sortTweets();
        hotness = this.getHotness();

        people *= wPeople;
        content *= wContent;
        hotness *= wHotness;
        score = people + content + hotness;
        //@TO DO: timeFactor routine
        // score*=timeFactor.get(this.tweets.get(tweets.size()-1).getTime());
        if (this.readMark)
            score *= 0.01;
        score += this.searchScore;
        //After each search, We need to clear the search score
        this.searchScore = 0;

        category = "default";
        if (people > content && people > hotness)
        {
            category = "people";
        }
        else if (content > people && content > hotness)
        {
            category = "content";
        }
        else if (hotness > people && hotness > content)
        {
            category = "hotness";
        }
    }
/*
    public void setVector()
    {
        this.setUrls();
        this.setKgrams();
        vec.addTerms(this.kgrams);
    }
*/
    public TermVector getVector()
    {
        return vec;
    }

    @Override
    public int compareTo(Conversation c)
    {
        double res = this.getRankScore() - c.getRankScore();
        if (res >= 0)
            return -1;
        else
            return 1;
    }

}
